package com.bian.spring.annotation.config;

import com.bian.spring.annotation.cd.AClass;
import com.bian.spring.annotation.cd.BClass;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.annotation.*;

/**
 * @author bian
 * @since 1.0
 * 2021/2/4
 *
 * Circular Dependency
 * 只有单实例的bean会【@Scope("singleton")】通过三级缓存提前暴露来解决循环依赖的问题，
 * 而非单实例的bean【@Scope("prototype")】每次从容器中获取的都是一个新的对象，都会重新重新创建，
 * 所以非单实例的bean没有缓存的，不会将其放到三级缓存中
 *
 * DefaultSingletonBeanRegistry.class
 * 第一级缓存（也叫单例池）Map<String, Object> singletonObjects：存放已经经历了完整生命周期的Bean对象
 * 第二级缓存Map<String, Object> earlySingletonObjects：存放早期暴露出来的Bean对象，Bean的生命周期未结束（属性还未填充完）{@link AbstractAutowireCapableBeanFactory#populateBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, org.springframework.beans.BeanWrapper)}
 * 第三级缓存Map<String, ObjectFactory<?>> singletonFactories：存放可以伸出bean的工厂
 *
 * 主要方法：getSingleton() -> doCreateBean() -> populateBean() -> addSingleton()
 *
 * 概念：实例化：内存中申请一块内存空间
 *      初始化：完成属性的各种赋值
 *
 * A、B两个对象在三级缓存中的迁移：
 * 1）、A 创建过程中需要B，于是A将自己放到三级缓存中，然后去实例化B
 * 2）、B 实例化的时候发现需要A，于是B先查一级缓存，没有再查二级缓存，还是没有，再查三级缓存，找到了A
 *      然后把三级缓存中的A放到二级缓存中，并删除三级缓存中的A
 * 3）、B 顺利的初始化完毕，将自己放到一级缓存中（此时B 里面的A还是处于创建中的状态），
 *      然后回来接着创建A，此时B已经创建结束，直接从一级缓存中拿到B，然后完成创建，并将A自己放到一级缓存中
 *
{@link DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)}
  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {              // isSingletonCurrentlyInCreation(beanName) 判断当前单实例bean是否正在创建中
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);                     // 一级缓存没有，就去二级缓存找
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  // 二级缓存中也没有，就去三级缓存找
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();                         // 三级缓存中有的话，就把他移动到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
   }
 *
 * 流程：
 * 1）、调用doGetBean()方法，想要获取beanA，于是调用getSingleton()从缓存中查找beanA
 * 2）、在getSingleton()中，从一级缓存查找，没有，返回null
 * 3）、doGetBean()方法中获取到的beanA为null，于是走对应的处理逻辑，调用getSingleton()的重载方法（参数为ObjectFactory）
 * 4）、在getSingleton()方法中，先将beanA_name添加到一个集合中，用于标记改bean正在创建中，然后回调匿名内部类的createBean()方法
 * 5）、进入AbstractAutowireCapableBeanFactory#doCreateBean，先反射调用构造器创建beanA的实例，然后判断：是否为单例、是否允许
 *      提前暴露引用（对于单例一般为true）、是否正在创建中（即是否在第四步的集合中），判断为true则将beanA添加到【三级缓存】中
 * 6）、对beanA进行属性填充，此时检测到beanA依赖于beanB，于是开始查找beanB
 * 7）、调用doGetBean()方法，和上述beanA的过程一样，到缓存中查找beanB，没有则创建，然后给beanB填充属性
 * 8）、此时beanB依赖于beanA，调用getSingleton()获取beanA，依次从一级、二级、三级缓存中找，此时从三级缓存中获取到beanA的创建工厂，
 *      通过创建工厂获取到singletonObject，此时该singletonObject指向的就是上面的doCreateBean()方法中实例化的beanA
 * 9）、这样beanB就获取到了beanA的依赖，于是beanB顺利完成实例化，并将beanA从三级缓存移到二级缓存中
 * 10）、随后beanA继续他的属性填充工作，此时也获取到了beanB，beanA也随之完成创建，回到getSingleton()方法中继续向下执行，将beanA从
 *      二级缓存移动到一级缓存中
 *
 */
@Configuration
@ComponentScan("com.bian.spring.annotation.cd")
public class MainConfigOfCD {

//    @Scope("singleton")
//    @Bean
//    public AClass aClass(BClass bClass) {
//        return new AClass(bClass);
//    }
//
//    @Scope("singleton")
//    @Bean
//    public BClass bClass(AClass aClass) {
//        return new BClass(aClass);
//    }
}
